/*
 Автор: Швейкин Евгений Юрьевич

 Имя модуля: mod_staining

 Функциональное описание:
    Общая формула
    y = (x - x_min)/(x_max - x_min) * (y_max - y_min) + y_min
    
    Если y_min = 0, а выражение не изменяющееся в течении кадра обозначим как k, то имеем
    y = k * (x - x_min), 
    где k = y_max/(x_max-x_min)
*/
`timescale 1ns / 10ps
//    ********************** НАЧАЛО МОДУЛЯ *********************************************************
module mod_staining
    #(  parameter width_p       = 320,
        parameter height_p      = 240,
        parameter dw_i_p        = 16,
        parameter dw_o_p        = 8

    )
    (   input  logic                    reset_n,
        input  logic                    clk,
        input  logic                    ena,
        

        input  logic                    wre_i,
        input  logic [dw_i_p - 1:0]     data_i,
        input  logic [31:0]             addr_i,
        
        output logic                    wre_o,
        output logic [dw_o_p - 1:0]     data_o,
        output logic [31:0]             addr_o,
        
        input  logic [15:0]             x_min,
        input  logic [15:0]             x_max
        
    );

// ********************** МАКРОСЫ ******************************************************************
    // Описывает процесс модуля выделения фронтов сигнала. 
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define front(clk, name)                    \
    logic name``_0;                             \
    logic name``_1;                             \
    logic name``_rise;                          \
    logic name``_fall;                          \
    logic name``_edge;                          \
    always_ff @(posedge clk)                    \
    begin                                       \
        name``_0 <= name;                       \
        name``_1 <= name``_0;                   \
        name``_rise <= name``_0 & (~name``_1);  \
        name``_fall <= name``_1 & (~name``_0);  \
        name``_edge <= name``_0 ^ name``_1;     \
    end

//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* КОНСТАНТЫ ****************************************************************

//  ********************* ТИПЫ ДАННЫХ **************************************************************

//  ********************* ПЕРЕМЕННЫЕ ***************************************************************

    // logic [dw_i_p - 1:0]        x_min;
    // logic [dw_i_p - 1:0]        x_max;
    logic [2*dw_i_p - 1:0]      x;
    
    logic [dw_i_p - 1:0]        x_min_latch;
    logic [dw_i_p - 1:0]        x_max_latch;
    logic [dw_i_p - 1:0]        x_sub_latch;
    logic [dw_i_p - 1:0]        quotient;
    logic [dw_i_p - 1:0]        k;

    logic                       req_divide;
    logic                       div_valid;    
//  ********************* БЛОК НЕПРЕРЫВНЫХ НАЗНАЧЕНИЙ ASSIGN ***************************************

//  ********************* ОПИСАНИЕ ПРОЦЕССОВ *******************************************************

    `front(clk, wre_i)
    `front(clk, div_valid)    
    
    /*
    always_ff @(posedge clk)
    begin
        if (wre_i) begin
            if (addr_i == 32'd0) begin      // По 0 адресу начальные значения
                x_min   <= 2**dw_i_p - 1;
                x_max   <= {dw_i_p{1'b0}};
            end
            else begin
                // Определение минимального
                if ((data_i < x_min) && (|data_i))
                    x_min   <= data_i;
                else;
                // Определение максимального
                if ((data_i > x_max) && (data_i < 2**dw_i_p - 1))
                    x_max   <= data_i;
                else;                
            end
        end
        else;        
    end
   */
    always_ff @(posedge clk)
    begin
        req_divide  <= 1'b0;
		  x_sub_latch     <= x_max_latch - x_min_latch;
        if (wre_i) begin
            if (addr_i == 32'd0) begin								// На первом пикселе кадра
								//(width_p * height_p-1)) begin     // На последнем пикселе кадра 
                x_min_latch     <= x_min;                   // защелкиваем значения
                x_max_latch     <= x_max;

                req_divide      <= 1'b1;                    // И ствим запрос на деление
            end
            else;
        end
        else;        
    end
    
    
    always_ff @(posedge clk)
    begin
        if (div_valid_rise) begin
            k <= quotient;
        end
        else;
        
        if (ena) begin
            if (wre_i) begin
                x  <= k * (data_i - x_min);
            end
            else;
            data_o  <= x[dw_i_p - 1:(dw_i_p - dw_o_p)];
        end
        else begin
            data_o  <= data_i;
        end
        addr_o  <= addr_i;
        wre_o   <= wre_i;
    end

//  ********************* ПОДКЛЮЧЕНИЕ МОДУЛЕЙ ******************************************************
    // Делим 2**dw_i_p/(x_max-x_min).
    mod_divide
    #(  .dw_p           (dw_i_p)
    )
    mod_divide_inst
    (   .reset          (~reset_n),
        .clk            (clk),
        .req            (req_divide),                           // запрос на деление
        .numer          (2**dw_i_p - 1),                        // делимое
        .denom          (x_sub_latch),                          // делитель
        .quotient       (quotient),                             // частное
        .remain         (),                                     // остаток
        .valid          (div_valid)
    );
    
endmodule